{{!

  Copyright (c) Facebook, Inc. and its affiliates.

  Licensed under the Apache License, Version 2.0 (the "License");
  you may not use this file except in compliance with the License.
  You may obtain a copy of the License at

      http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.

}}{{!

Python wrappers of the structs defined in the services files. This file is
compiled into it's own module to be included by clients and services and
end-user code. It's one of the more complicated files, as it has to map
Pythonic APIs to C++ objects and back.

One of the nastier things in this file is the definition of containers.
A separate container wrapper has to be defined for each type of contained
attribute because Cython can't template C++ classes. So, for example, we need
a List__int16 or a List__string or a Map__string_mystruct instance for each
container/type combination. Consider that containers can contain other containers
or structs that contain containers and you realize how messy this can get.
Further, we'd prefer to have the end user freed from having to know about these
container types, so we'll need to define factories for them based on what they
want to include.

}}
{{> common/AutoGeneratedPy}}
cimport cython as __cython
from cpython.bytes cimport PyBytes_AsStringAndSize
from cpython.object cimport PyTypeObject, Py_LT, Py_LE, Py_EQ, Py_NE, Py_GT, Py_GE
from libcpp.memory cimport shared_ptr, make_shared, unique_ptr, make_unique
from libcpp.string cimport string
from libcpp cimport bool as cbool
from libcpp.iterator cimport inserter as cinserter
from libcpp.utility cimport move as cmove
from cpython cimport bool as pbool
from cython.operator cimport dereference as deref, preincrement as inc, address as ptr_address
import thrift.py3.types
cimport thrift.py3.types
cimport thrift.py3.exceptions
from thrift.py3.types cimport (
    translate_cpp_enum_to_python,
    SetMetaClass as __SetMetaClass,
    const_pointer_cast,
    constant_shared_ptr,
    default_inst,
    reference_shared_ptr as __reference_shared_ptr,
    NOTSET as __NOTSET,
    EnumData as __EnumData,
    EnumFlagsData as __EnumFlagsData,
    UnionTypeEnumData as __UnionTypeEnumData,
    createEnumDataForUnionType as __createEnumDataForUnionType,
)
cimport thrift.py3.std_libcpp as std_libcpp
cimport thrift.py3.serializer as serializer
import folly.iobuf as __iobuf
from folly.optional cimport cOptional
from folly.memory cimport to_shared_ptr as __to_shared_ptr

import sys
from collections.abc import Sequence, Set, Mapping, Iterable
import weakref as __weakref
import builtins as _builtins
{{#program:has_stream?}}
import asyncio
from folly.coro cimport bridgeCoroTaskWith
{{/program:has_stream?}}
{{#program:includeNamespaces}}
{{#hasTypes?}}
cimport {{#includeNamespace}}{{value}}.{{/includeNamespace}}types as _{{#includeNamespace}}{{value}}_{{/includeNamespace}}types
import {{#includeNamespace}}{{value}}.{{/includeNamespace}}types as _{{#includeNamespace}}{{value}}_{{/includeNamespace}}types
{{/hasTypes?}}
{{/program:includeNamespaces}}

cimport {{#program:py3Namespaces}}{{value}}.{{/program:py3Namespaces}}{{program:name}}.types_reflection as _types_reflection

{{> types/enum }}
{{> types/unionTypeEnum }}

{{#program:structs}}
{{^struct:union?}}
@__cython.auto_pickle(False)
cdef class {{struct:name}}({{> types/PythonStructClass}}):

    {{^struct:exception?}}
    def __init__(
        {{struct:name}} self, *{{#struct:fields}},
        {{#field:type}}{{!
            }}{{!  # If we can set a None then type it
                }}{{#type:cythonTypeNoneable?}}{{!
                    }}{{> types/CythonPythonType}} {{!
                }}{{/type:cythonTypeNoneable?}}{{!
            }}{{field:py_name}}{{!
                }}=None{{!
        }}{{/field:type}}{{/struct:fields}}
    ):
    {{/struct:exception?}}
    {{#struct:exception?}}{{! Python exceptions are weird about keyword only}}
    def __init__(
        {{struct:name}} self{{#struct:fields}},
        {{#field:type}}{{!
                }}{{#type:cythonTypeNoneable?}}{{!
                    }}{{> types/CythonPythonType}} {{!
                }}{{/type:cythonTypeNoneable?}}{{!
            }}{{field:py_name}}=None{{!
        }}{{/field:type}}{{/struct:fields}}
    ):
        {{#struct:fields}}
        {{/struct:fields}}
    {{/struct:exception?}}
        {{#struct:fields}}
        {{#field:type}}
        {{^type:cythonTypeNoneable?}}
        {{^type:container?}}
        if {{field:py_name}} is not None:
            {{#type:number?}}
            if not isinstance({{field:py_name}}, {{> types/PythonIsInstanceType}}):
                raise TypeError(f'{{field:py_name}} is not a { {{> types/PythonType}} !r}.')
            {{#type:integer?}}
            {{! inject cython int overflow checks }}
            {{field:py_name}} = <{{> types/CythonPythonType}}> {{field:py_name}}
            {{/type:integer?}}
            {{/type:number?}}

        {{/type:container?}}
        {{/type:cythonTypeNoneable?}}
        {{/field:type}}
        {{/struct:fields}}
        self._cpp_obj = __to_shared_ptr(cmove({{struct:name}}._make_instance(
          NULL,
          NULL,{{#struct:fields}}
          {{field:py_name}},{{/struct:fields}}
        )))
        {{#struct:exception?}}
        _builtins.Exception.__init__(self, {{#struct:fields}}self.{{field:py_name}}{{^last?}}, {{/last?}}{{/struct:fields}})
        {{/struct:exception?}}

    {{^struct:exception?}}
    {{^struct:cpp_noncopyable?}}
    def __call__(
        {{struct:name}} self{{#struct:fields}},
        {{field:py_name}}=__NOTSET{{/struct:fields}}
    ):
        {{^struct:fields?}}
        return self
        {{/struct:fields?}}
        {{#struct:fields?}}
        ___NOTSET = __NOTSET  # Cheaper for larger structs
        cdef bint[{{struct:size}}] __isNOTSET  # so make_instance is typed

        __fbthrift_changed = False
        {{#struct:fields}}
        if {{field:py_name}} is ___NOTSET:
            __isNOTSET[{{field:index}}] = True
            {{field:py_name}} = None
        else:
            __isNOTSET[{{field:index}}] = False
            __fbthrift_changed = True

        {{/struct:fields}}

        if not __fbthrift_changed:
            return self
        {{/struct:fields?}}
        {{#struct:fields}}
        {{#first?}}

        {{/first?}}
        {{#field:type}}
        {{^type:container?}}
        if {{field:py_name}} is not None:
            {{#type:enum?}}
            if not isinstance({{field:py_name}}, {{> types/PythonType}}):
                raise TypeError(f'field {{field:py_name}} value: { {{field:py_name}} !r} is not of the enum type { {{> types/PythonType}} }.')
            {{/type:enum?}}
            {{^type:enum?}}
            if not isinstance({{field:py_name}}, {{> types/PythonIsInstanceType}}):
                raise TypeError(f'{{field:py_name}} is not a { {{> types/PythonType}} !r}.')
            {{#type:integer?}}
            {{! inject cython int overflow checks }}
            {{field:py_name}} = <{{> types/CythonPythonType}}> {{field:py_name}}
            {{/type:integer?}}
            {{/type:enum?}}

        {{/type:container?}}
        {{/field:type}}
        {{/struct:fields}}
        {{#struct:fields?}}
        __fbthrift_inst = <{{struct:name}}>{{struct:name}}.__new__({{struct:name}})
        __fbthrift_inst._cpp_obj = __to_shared_ptr(cmove({{struct:name}}._make_instance(
          self._cpp_obj.get(),
          __isNOTSET,{{#struct:fields}}
          {{field:py_name}},{{/struct:fields}}
        )))
        return __fbthrift_inst
        {{/struct:fields?}}
    {{/struct:cpp_noncopyable?}}
    {{/struct:exception?}}

    @staticmethod
    cdef unique_ptr[c{{struct:name}}] _make_instance(
        c{{struct:name}}* base_instance,
        bint* __isNOTSET{{#struct:fields}},
        {{#field:type}}{{!
            }}{{#type:cythonTypeNoneable?}}{{!
                }}{{> types/CythonPythonType}} {{!
            }}{{/type:cythonTypeNoneable?}}{{!
            }}{{^type:cythonTypeNoneable?}}{{!
                }}object {{!
            }}{{/type:cythonTypeNoneable?}}{{!
            }}{{field:py_name}}{{!
        }} {{/field:type}}{{/struct:fields}}
    ) except *:
        cdef unique_ptr[c{{struct:name}}] c_inst
        if base_instance:
            {{#struct:cpp_noncopyable?}}
            raise TypeError("{{struct:name}} is noncopyable")
            {{/struct:cpp_noncopyable?}}
            {{^struct:cpp_noncopyable?}}
            c_inst = make_unique[c{{struct:name}}](deref(base_instance))
            {{/struct:cpp_noncopyable?}}
        else:
            c_inst = make_unique[c{{struct:name}}]()

        {{! We never have a base_instance for exceptions (because they have
            no __call__, so don't generate this part for them because it
            references the _defaults object, which they also don't have )}}
        {{^struct:exception?}}
        if base_instance:
            # Convert None's to default value. (or unset)
            {{#struct:fields}}
            if not __isNOTSET[{{field:index}}] and {{field:py_name}} is None:
                {{#field:hasDefaultValue?}}
                {{#field:has_ref_accessor?}}
                deref(c_inst).{{field:py_name}}_ref().assign(default_inst[c{{struct:name}}]().{{> CythonFieldValueRef}})
                {{/field:has_ref_accessor?}}
                {{^field:has_ref_accessor?}}
                deref(c_inst).{{field:py_name}} = default_inst[c{{struct:name}}]().{{> CythonFieldValueRef}}
                {{/field:has_ref_accessor?}}
                {{/field:hasDefaultValue?}}
                {{#field:isset?}}
                deref(c_inst).__isset.{{field:py_name}} = False
                {{/field:isset?}}
                {{#field:reference?}}
                deref(c_inst).{{field:py_name}}.reset()
                {{/field:reference?}}
                pass

            {{/struct:fields}}{{!
            }}{{^struct:fields}}
            pass
            {{/struct:fields}}
        {{/struct:exception?}}
        {{#struct:fields}}{{#field:type}}
        if {{field:py_name}} is not None:
            {{#field:has_ref_accessor?}}
            deref(c_inst).{{field:py_name}}_ref().assign({{> CythonFieldValueForAssignment}})
            {{/field:has_ref_accessor?}}
            {{^field:has_ref_accessor?}}
            deref(c_inst).{{field:py_name}} = {{> CythonFieldValueForAssignment}}
            {{/field:has_ref_accessor?}}
            {{! This should also be inside the if statement, so indent appropriately }}
            {{#field:isset?}}
            deref(c_inst).__isset.{{field:py_name}} = True
            {{/field:isset?}}
        {{/field:type}}{{/struct:fields}}
        # in C++ you don't have to call move(), but this doesn't translate
        # into a C++ return statement, so you do here
        return cmove(c_inst)

    cdef object __fbthrift_isset(self):
        return thrift.py3.types._IsSet("{{struct:name}}", {
        {{#struct:fields}}
        {{#field:has_ref_accessor?}}
          "{{field:py_name}}": deref(self._cpp_obj).{{field:py_name}}_ref().has_value(),
        {{/field:has_ref_accessor?}}
        {{/struct:fields}}
        })

    def __iter__(self):
        {{#struct:fields}}
        yield '{{field:py_name}}', self.{{field:py_name}}
        {{/struct:fields}}
        {{^struct:fields}}
        yield from ()
        {{/struct:fields}}

    @staticmethod
    cdef create(shared_ptr[c{{struct:name}}] cpp_obj):
        __fbthrift_inst = <{{struct:name}}>{{struct:name}}.__new__({{struct:name}}{{#struct:exception?}}, (<bytes>deref(cpp_obj).what()).decode('utf-8'){{/struct:exception?}})
        __fbthrift_inst._cpp_obj = cmove(cpp_obj)
        {{#struct:exception?}}
        _builtins.Exception.__init__(__fbthrift_inst, {{#struct:fields}}__fbthrift_inst.{{field:py_name}}{{^last?}}, {{/last?}}{{/struct:fields}})
        {{/struct:exception?}}
        return __fbthrift_inst

    {{#struct:fields}}
    @property
    def {{field:py_name}}(self):
        {{#field:optional?}}
        {{^field:hasDefaultValue?}}
        {{#field:isset?}}
        if not deref(self._cpp_obj).__isset.{{field:py_name}}:
            return None
        {{/field:isset?}}
        {{/field:hasDefaultValue?}}
        {{/field:optional?}}
        {{#field:type}}

        {{> CythonStructGetter}}

        {{/field:type}}

    {{/struct:fields}}

    def __hash__({{struct:name}} self):
        return  super().__hash__()

    {{#struct:exception?}}
    {{#struct:exception_message?}}
    def __str__({{struct:name}} self):
        field = self.{{struct:exception_message}}
        if field is None:
            {{! optional field, stringify }}
            return str(field)
        return field

    {{/struct:exception_message?}}
    {{/struct:exception?}}
{{/struct:union?}}{{!
}}{{#struct:union?}}


@__cython.auto_pickle(False)
cdef class {{struct:name}}(thrift.py3.types.Union):
    Type = __{{struct:name}}Type

    def __init__(
        self, *{{#struct:fields}},
        {{#field:type}}{{!
            }}{{#type:cythonTypeNoneable?}}{{!
                }}{{> types/CythonPythonType}} {{!
            }}{{/type:cythonTypeNoneable?}}{{!
        }}{{/field:type}}{{!
            }}{{field:py_name}}=None{{!
        }}{{/struct:fields}}
    ):
        {{#struct:fields}}
        {{#field:type}}
        {{^type:cythonTypeNoneable?}}
        {{^type:container?}}
        if {{field:py_name}} is not None:
            if not isinstance({{field:py_name}}, {{> types/PythonIsInstanceType}}):
                raise TypeError(f'{{field:py_name}} is not a { {{> types/PythonType}} !r}.')
            {{#type:integer?}}
            {{! inject cython int overflow checks }}
            {{field:py_name}} = <{{> types/CythonPythonType}}> {{field:py_name}}
            {{/type:integer?}}

        {{/type:container?}}
        {{/type:cythonTypeNoneable?}}
        {{/field:type}}
        {{/struct:fields}}
        self._cpp_obj = __to_shared_ptr(cmove({{struct:name}}._make_instance(
          NULL,{{#struct:fields}}
          {{field:py_name}},{{/struct:fields}}
        )))
        self._load_cache()

    @staticmethod
    def fromValue(value):
        if value is None:
            return {{struct:name}}()
        {{! We do this with strict types first, then we will do int to float conversion}}
        {{#struct:fields}}
        {{#field:type}}
        if isinstance(value, {{> types/PythonType}}):
            {{#type:number?}}
            if not isinstance(value, pbool):
                try:
                    {{#type:integer?}}
                    {{! Cython does OverflowError checking for us }}
                    <{{> types/CythonPythonType}}> value
                    {{/type:integer?}}
                    {{#type:float?}}
                    {{! This will probably fail most of the time
                        if it does then when we try again to use floating point
                        below it will just accept the loss of precision,
                        or just maybe there is a double field comming up }}
                    if <{{> types/CythonPythonType}}>value != value:
                        raise OverflowError
                    {{/type:float?}}
                    return {{struct:name}}({{field:py_name}}=value)
                except OverflowError:
                    pass
            {{/type:number?}}
            {{^type:number?}}
            return {{struct:name}}({{field:py_name}}=value)
            {{/type:number?}}
        {{/field:type}}
        {{/struct:fields}}
        {{#struct:fields}}
        {{#field:type}}
        {{#type:floating_point?}}
        if isinstance(value, {{> types/PythonIsInstanceType}}):
            try:
                <{{> types/CythonPythonType}}> value
                return {{struct:name}}({{field:py_name}}=value)
            except OverflowError:
                pass
        {{/type:floating_point?}}
        {{/field:type}}
        {{/struct:fields}}
        raise ValueError(f"Unable to derive correct union field for value: {value}")

    @staticmethod
    cdef unique_ptr[c{{struct:name}}] _make_instance(
        c{{struct:name}}* base_instance{{#struct:fields}},
        {{#field:type}}{{!
            }}{{#type:cythonTypeNoneable?}}{{!
                }}{{> types/CythonPythonType}} {{!
            }}{{/type:cythonTypeNoneable?}}{{!
            }}{{^type:cythonTypeNoneable?}}{{!
                }}object {{!
            }}{{/type:cythonTypeNoneable?}}{{!
            }}{{field:py_name}}{{!
        }}{{/field:type}}{{/struct:fields}}
    ) except *:
        cdef unique_ptr[c{{struct:name}}] c_inst = make_unique[c{{struct:name}}]()
        cdef bint any_set = False
        {{#struct:fields}}{{#field:type}}
        if {{field:py_name}} is not None:
            if any_set:
                raise TypeError("At most one field may be set when initializing a union")
            {{> CythonUnionAssignField}}

            any_set = True
        {{/field:type}}{{/struct:fields}}
        # in C++ you don't have to call move(), but this doesn't translate
        # into a C++ return statement, so you do here
        return cmove(c_inst)

    @staticmethod
    cdef create(shared_ptr[c{{struct:name}}] cpp_obj):
        __fbthrift_inst = <{{struct:name}}>{{struct:name}}.__new__({{struct:name}})
        __fbthrift_inst._cpp_obj = cmove(cpp_obj)
        __fbthrift_inst._load_cache()
        return __fbthrift_inst

    {{#struct:fields}}
    @property
    def {{field:py_name}}(self):
        if self.type.value != {{field:key}}:
            raise TypeError(f'Union contains a value of type {self.type.name}, not {{field:py_name}}')
        return self.value

    {{/struct:fields}}

    def __hash__({{struct:name}} self):
        return  super().__hash__()

    cdef _load_cache({{struct:name}} self):
        self.type = {{struct:name}}.Type(<int>(deref(self._cpp_obj).getType()))
        cdef int type = self.type.value
        if type == 0:    # Empty
            self.value = None
        {{#struct:fields}}
        elif type == {{field:key}}:
            {{#field:type}}
            {{> CythonUnionGetter}}
            {{/field:type}}

        {{/struct:fields}}

{{/struct:union?}}
    {{! Below are some things that are common to structs and unions: }}
    def __copy__({{struct:name}} self):
        {{#struct:cpp_noncopyable?}}
        raise TypeError("{{struct:name}} is noncopyable")
        {{/struct:cpp_noncopyable?}}
        {{^struct:cpp_noncopyable?}}
        cdef shared_ptr[c{{struct:name}}] cpp_obj = make_shared[c{{struct:name}}](
            deref(self._cpp_obj)
        )
        return {{struct:name}}.create(cmove(cpp_obj))
        {{/struct:cpp_noncopyable?}}

    def __richcmp__(self, other, op):
        cdef int cop = op
        if not (
                isinstance(self, {{struct:name}}) and
                isinstance(other, {{struct:name}})):
            if cop == Py_EQ:  # different types are never equal
                return False
            elif cop == Py_NE:  # different types are always notequal
                return True
            else:
                return NotImplemented

        {{#struct:cpp_noncomparable}}
        {{! There are no comparable ops on C++ class so we do default py_object behavior }}
        if cop == Py_EQ:
            return self.__noncomparable_eq(other)
        elif cop == Py_NE:
            return not self.__noncomparable_eq(other)
        {{/struct:cpp_noncomparable}}
        {{^struct:cpp_noncomparable}}
        cdef c{{struct:name}}* cself = (<{{struct:name}}>self)._cpp_obj.get()
        cdef c{{struct:name}}* cother = (<{{struct:name}}>other)._cpp_obj.get()
        if cop == Py_EQ:
            return deref(cself) == deref(cother)
        elif cop == Py_NE:
            return deref(cself) != deref(cother)
        {{#struct:is_struct_orderable?}}
        elif cop == Py_LT:
            return deref(cself) < deref(cother)
        elif cop == Py_LE:
            return deref(cself) <= deref(cother)
        elif cop == Py_GT:
            return deref(cself) > deref(cother)
        elif cop == Py_GE:
            return deref(cself) >= deref(cother)
        {{/struct:is_struct_orderable?}}
        {{/struct:cpp_noncomparable}}
        else:
            return NotImplemented

    @staticmethod
    def __get_reflection__():
        return _types_reflection.get_reflection__{{struct:name}}()

    {{^struct:exception?}}
    cdef __iobuf.IOBuf _serialize({{struct:name}} self, __Protocol proto):
        cdef unique_ptr[__iobuf.cIOBuf] data
        with nogil:
            data = cmove(serializer.cserialize[c{{struct:name}}](self._cpp_obj.get(), proto))
        return __iobuf.from_unique_ptr(cmove(data))

    cdef cuint32_t _deserialize({{struct:name}} self, const __iobuf.cIOBuf* buf, __Protocol proto) except? 0:
        cdef cuint32_t needed
        {{!
            This is a special case, we need to construct an empty _cpp_obj because
            thrift.py3.serializer.deserialize will just call __new__ to skip
            all of our runtime type checks. We do it like this because
            thrift.py3.serializer.deserialize does not have enough type information
            to call the staticmethod .create()
        }}
        self._cpp_obj = make_shared[c{{struct:name}}]()
        with nogil:
            needed = serializer.cdeserialize[c{{struct:name}}](buf, self._cpp_obj.get(), proto)
        {{#struct:union?}}
        # force a cache reload since the underlying data's changed
        self._load_cache()
        {{/struct:union?}}
        return needed
    {{/struct:exception?}}


{{/program:structs}}
{{#program:containerTypes}}
@__cython.auto_pickle(False)
cdef class {{> types/CythonPythonType}}(thrift.py3.types.{{!
        }}{{#type:list?}}List{{/type:list?}}{{!
        }}{{#type:set?}}Set{{/type:set?}}{{!
        }}{{#type:map?}}Map{{/type:map?}}{{!
        }}):
    def __init__(self, items=None):
        if isinstance(items, {{> types/CythonPythonType}}):
            self._cpp_obj = (<{{> types/CythonPythonType}}> items)._cpp_obj
        else:
            self._cpp_obj = {{> types/CythonPythonType}}._make_instance(items)

    @staticmethod
    cdef create(shared_ptr[{{> types/CythonCppType}}] c_items):
        __fbthrift_inst = <{{> types/CythonPythonType}}>{{> types/CythonPythonType}}.__new__({{> types/CythonPythonType}})
        __fbthrift_inst._cpp_obj = cmove(c_items)
        return __fbthrift_inst

    def __copy__({{> types/CythonPythonType}} self):
        cdef shared_ptr[{{> types/CythonCppType}}] cpp_obj = make_shared[{{> types/CythonCppType}}](
            deref(self._cpp_obj)
        )
        return {{> types/CythonPythonType}}.create(cmove(cpp_obj))

    def __len__(self):
        return deref(self._cpp_obj).size()

{{#type:list?}}
    @staticmethod
    cdef shared_ptr[{{> types/CythonCppType}}] _make_instance(object items) except *:
        cdef shared_ptr[{{> types/CythonCppType}}] c_inst = make_shared[{{> types/CythonCppType}}]()
        if items is not None:
            {{#type:containerOfString?}}
            if isinstance(items, str):
                raise TypeError("If you really want to pass a string into a {{> types/PEP484Type}} field, explicitly convert it first.")
            {{/type:containerOfString?}}
            for item in items:
                {{#type:listElemType}}
                {{#type:container?}}
                if item is None:
                    raise TypeError("None is not of the type {{> types/PEP484Type}}")
                if not isinstance(item, {{> types/PythonType}}):
                    item = {{> types/PythonType}}(item)
                {{/type:container?}}
                {{^type:container?}}
                if not isinstance(item, {{> types/PythonIsInstanceType}}):
                    raise TypeError(f"{item!r} is not of type {{> types/PEP484Type}}")
                {{#type:integer?}}
                {{! inject cython int overflow checks }}
                item = <{{> types/CythonPythonType}}> item
                {{/type:integer?}}
                {{/type:container?}}
                deref(c_inst).push_back({{> CythonPythonToCppItem}})
                {{/type:listElemType}}
        return c_inst

    def __getitem__(self, object index_obj):
        cdef shared_ptr[{{> types/CythonCppType}}] c_inst
        cdef {{#type:listElemType}}{{!
            }}{{^type:simple?}}shared_ptr[{{/type:simple?}}{{> types/CythonCppType}}{{!
            }}{{^type:simple?}}]{{/type:simple?}}{{!
            }}{{/type:listElemType}} citem
        if isinstance(index_obj, slice):
            c_inst = make_shared[{{> types/CythonCppType}}]()
            sz = deref(self._cpp_obj).size()
            for index in range(*index_obj.indices(sz)):
                deref(c_inst).push_back(deref(self._cpp_obj)[index])
            return {{> types/CythonPythonType}}.create(cmove(c_inst))
        else:
            index = <int?>index_obj
            size = len(self)
            # Convert a negative index
            if index < 0:
                index = size + index
            if index >= size or index < 0:
                raise IndexError('list index out of range')
            {{#type:listElemType}}
            citem = {{^type:simple?}}__reference_shared_ptr({{/type:simple?}}{{!
                    }}deref(self._cpp_obj)[index]{{!
                    }}{{^type:simple?}}, self._cpp_obj){{/type:simple?}}
            return {{> ContainerCythonCppToPythonItem}}
            {{/type:listElemType}}

    def __contains__(self, item):
        if not self or item is None:
            return False
        {{#type:listElemType}}
        {{#type:container?}}
        try:
            if not isinstance(item, {{> types/PythonType}}):
                item = {{> types/PythonType}}(item)
        except Exception:
            return False
        {{/type:container?}}
        if not isinstance(item, {{> types/PythonType}}):
            return False
        {{/type:listElemType}}
        return std_libcpp.find[{{>types/CythonCppType}}.iterator, {{#type:listElemType}}{{>types/CythonCppType}}](deref(self._cpp_obj).begin(), deref(self._cpp_obj).end(), {{> CythonPythonToCppItem}}) != deref(self._cpp_obj).end()
        {{/type:listElemType}}

    def __iter__(self):
        if not self:
            return
        cdef {{#type:listElemType}}{{!
            }}{{^type:simple?}}shared_ptr[{{/type:simple?}}{{> types/CythonCppType}}{{!
            }}{{^type:simple?}}]{{/type:simple?}}{{!
            }}{{/type:listElemType}} citem
        cdef {{>types/CythonCppType}}.iterator loc = deref(self._cpp_obj).begin()
        while loc != deref(self._cpp_obj).end():
            {{#type:listElemType}}
            citem = {{^type:simple?}}__reference_shared_ptr({{/type:simple?}}{{!
                    }}deref(loc){{!
                    }}{{^type:simple?}}, self._cpp_obj){{/type:simple?}}
            yield {{> ContainerCythonCppToPythonItem}}
            {{/type:listElemType}}
            inc(loc)

    def __reversed__(self):
        if not self:
            return
        cdef {{#type:listElemType}}{{!
            }}{{^type:simple?}}shared_ptr[{{/type:simple?}}{{> types/CythonCppType}}{{!
            }}{{^type:simple?}}]{{/type:simple?}}{{!
            }}{{/type:listElemType}} citem
        cdef {{>types/CythonCppType}}.reverse_iterator loc = deref(self._cpp_obj).rbegin()
        while loc != deref(self._cpp_obj).rend():
            {{#type:listElemType}}
            citem = {{^type:simple?}}__reference_shared_ptr({{/type:simple?}}{{!
                    }}deref(loc){{!
                    }}{{^type:simple?}}, self._cpp_obj){{/type:simple?}}
            yield {{> ContainerCythonCppToPythonItem}}
            {{/type:listElemType}}
            inc(loc)

    def index(self, item, start not None=__NOTSET, stop not None=__NOTSET):
        err = ValueError(f'{item} is not in list')
        if not self or item is None:
            raise err
        offset_begin = offset_end = 0
        if stop is not __NOTSET or start is not __NOTSET:
            # Like self[start:stop].index(item)
            size = len(self)
            stop = stop if stop is not __NOTSET else size
            start = start if start is not __NOTSET else 0
            # Convert stop to a negative position.
            if stop > 0:
                stop = min(stop - size, 0)
            if stop <= -size:
                raise err  # List would be empty
            offset_end = -stop
            # Convert start to always be positive
            if start < 0:
                start = max(size + start, 0)
            if start >= size:
                raise err  # past end of list
            offset_begin = start

        {{#type:listElemType}}
        {{#type:container?}}
        try:
            if not isinstance(item, {{> types/PythonType}}):
                item = {{> types/PythonType}}(item)
        except Exception:
            raise err from None
        {{/type:container?}}
        if not isinstance(item, {{> types/PythonType}}):
            raise err
        {{/type:listElemType}}
        cdef {{>types/CythonCppType}}.iterator end = std_libcpp.prev(deref(self._cpp_obj).end(), <cint64_t>offset_end)
        cdef {{>types/CythonCppType}}.iterator loc = {{!
            }}std_libcpp.find[{{>types/CythonCppType}}.iterator, {{#type:listElemType}}{{>types/CythonCppType}}](
            std_libcpp.next(deref(self._cpp_obj).begin(), <cint64_t>offset_begin),
            end,
            {{> CythonPythonToCppItem}}
        )
        {{/type:listElemType}}
        if loc != end:
            return <cint64_t> std_libcpp.distance(deref(self._cpp_obj).begin(), loc)
        raise err

    def count(self, item):
        if not self or item is None:
            return 0
        {{#type:listElemType}}
        {{#type:container?}}
        try:
            if not isinstance(item, {{> types/PythonType}}):
                item = {{> types/PythonType}}(item)
        except Exception:
            return 0
        {{/type:container?}}
        if not isinstance(item, {{> types/PythonType}}):
            return 0
        {{/type:listElemType}}
        return <cint64_t> std_libcpp.count[{{>types/CythonCppType}}.iterator, {{#type:listElemType}}{{>types/CythonCppType}}](
            deref(self._cpp_obj).begin(), deref(self._cpp_obj).end(), {{> CythonPythonToCppItem}})
        {{/type:listElemType}}

    @staticmethod
    def __get_reflection__():
        return _types_reflection.get_reflection__{{> types/CythonPythonType}}()


Sequence.register({{> types/CythonPythonType}})
{{/type:list?}}
{{#type:set?}}
    @staticmethod
    cdef shared_ptr[{{> types/CythonCppType}}] _make_instance(object items) except *:
        cdef shared_ptr[{{> types/CythonCppType}}] c_inst = make_shared[{{> types/CythonCppType}}]()
        if items is not None:
            {{#type:containerOfString?}}
            if isinstance(items, str):
                raise TypeError("If you really want to pass a string into a {{> types/PEP484Type}} field, explicitly convert it first.")
            {{/type:containerOfString?}}
            for item in items:
                {{#type:setElemType}}
                {{#type:container?}}
                if item is None:
                    raise TypeError("None is not of type {{> types/PEP484Type}}")
                if not isinstance(item, {{> types/PythonType}}):
                    item = {{> types/PythonType}}(item)
                {{/type:container?}}
                {{^type:container?}}
                if not isinstance(item, {{> types/PythonIsInstanceType}}):
                    raise TypeError(f"{item!r} is not of type {{> types/PEP484Type}}")
                {{#type:integer?}}
                {{! inject cython int overflow checks }}
                item = <{{> types/CythonPythonType}}> item
                {{/type:integer?}}
                {{/type:container?}}
                deref(c_inst).insert({{> CythonPythonToCppItem}})
                {{/type:setElemType}}
        return c_inst

    def __contains__(self, item):
        if not self or item is None:
            return False
        {{#type:setElemType}}
        {{#type:container?}}
        try:
            if not isinstance(item, {{> types/PythonType}}):
                item = {{> types/PythonType}}(item)
        except Exception:
            return False
        {{/type:container?}}
        if not isinstance(item, {{> types/PythonType}}):
            return False
        return pbool(deref(self._cpp_obj).count({{>CythonPythonToCppItem}}))
        {{/type:setElemType}}


    def __iter__(self):
        if not self:
            return
        cdef {{#type:setElemType}}{{!
            }}{{^type:simple?}}shared_ptr[{{/type:simple?}}{{> types/CythonCppType}}{{!
            }}{{^type:simple?}}]{{/type:simple?}}{{!
            }}{{/type:setElemType}} citem
        cdef {{>types/CythonCppType}}.iterator loc = deref(self._cpp_obj).begin()
        while loc != deref(self._cpp_obj).end():
            {{#type:setElemType}}
            citem = {{^type:simple?}}__reference_shared_ptr({{/type:simple?}}{{!
                    }}deref(loc){{!
                    }}{{^type:simple?}}, self._cpp_obj){{/type:simple?}}
            yield {{> ContainerCythonCppToPythonItem}}
            {{/type:setElemType}}
            inc(loc)

    def __hash__(self):
        return super().__hash__()

    def __richcmp__(self, other, op):
        cdef int cop = op
        cdef shared_ptr[{{> types/CythonCppType}}] cself, cother
        cdef {{>types/CythonCppType}}.iterator loc
        if (isinstance(self, {{> types/CythonPythonType}}) and
                isinstance(other, {{> types/CythonPythonType}})):
            cself = (<{{> types/CythonPythonType}}> self)._cpp_obj
            cother = (<{{> types/CythonPythonType}}> other)._cpp_obj
            # C level comparisons
            if cop == Py_LT:    # Less Than (strict subset)
                if not deref(cself).size() < deref(cother).size():
                    return False
                loc = deref(cself).begin()
                while loc != deref(cself).end():
                    if not deref(cother).count(deref(loc)):
                        return False
                    inc(loc)
                return True
            elif cop == Py_LE:  # Less Than or Equal To  (subset)
                loc = deref(cself).begin()
                while loc != deref(cself).end():
                    if not deref(cother).count(deref(loc)):
                        return False
                    inc(loc)
                return True
            elif cop == Py_EQ:  # Equivalent
                if deref(cself).size() != deref(cother).size():
                    return False
                loc = deref(cself).begin()
                while loc != deref(cself).end():
                    if not deref(cother).count(deref(loc)):
                        return False
                    inc(loc)
                return True
            elif cop == Py_NE:  # Not Equivalent
                loc = deref(cself).begin()
                while loc != deref(cself).end():
                    if not deref(cother).count(deref(loc)):
                        return True
                    inc(loc)
                return deref(cself).size() != deref(cother).size()
            elif cop == Py_GT:  # Greater Than (strict superset)
                if not deref(cself).size() > deref(cother).size():
                    return False
                loc = deref(cother).begin()
                while loc != deref(cother).end():
                    if not deref(cself).count(deref(loc)):
                        return False
                    inc(loc)
                return True
            elif cop == Py_GE:  # Greater Than or Equal To (superset)
                loc = deref(cother).begin()
                while loc != deref(cother).end():
                    if not deref(cself).count(deref(loc)):
                        return False
                    inc(loc)
                return True

        # Python level comparisons
        if cop == Py_LT:
            return Set.__lt__(self, other)
        elif cop == Py_LE:
            return Set.__le__(self, other)
        elif cop == Py_EQ:
            return Set.__eq__(self, other)
        elif cop == Py_NE:
            return Set.__ne__(self, other)
        elif cop == Py_GT:
            return Set.__gt__(self, other)
        elif cop == Py_GE:
            return Set.__ge__(self, other)

    def __and__(self, other):
        if not isinstance(self, {{> types/CythonPythonType}}):
            self = {{> types/CythonPythonType}}(self)
        if not isinstance(other, {{> types/CythonPythonType}}):
            other = {{> types/CythonPythonType}}(other)

        cdef shared_ptr[{{> types/CythonCppType}}] shretval = \
            make_shared[{{> types/CythonCppType}}]()

        cdef shared_ptr[{{> types/CythonCppType}}] cself = (<{{> types/CythonPythonType}}> self)._cpp_obj
        cdef shared_ptr[{{> types/CythonCppType}}] cother = (<{{> types/CythonPythonType}}> other)._cpp_obj

        cdef {{>types/CythonCppType}}.iterator loc = deref(cself).begin()
        while loc != deref(cself).end():
            if deref(cother).count(deref(loc)) > 0:
                deref(shretval).insert(deref(loc))
            inc(loc)
        return {{> types/CythonPythonType}}.create(cmove(shretval))

    def __sub__(self, other):
        if not isinstance(self, {{> types/CythonPythonType}}):
            self = {{> types/CythonPythonType}}(self)
        if not isinstance(other, {{> types/CythonPythonType}}):
            other = {{> types/CythonPythonType}}(other)

        cdef shared_ptr[{{> types/CythonCppType}}] shretval = \
            make_shared[{{> types/CythonCppType}}]()

        cdef shared_ptr[{{> types/CythonCppType}}] cself = (<{{> types/CythonPythonType}}> self)._cpp_obj
        cdef shared_ptr[{{> types/CythonCppType}}] cother = (<{{> types/CythonPythonType}}> other)._cpp_obj

        cdef {{>types/CythonCppType}}.iterator loc = deref(cself).begin()
        while loc != deref(cself).end():
            if deref(cother).count(deref(loc)) == 0:
                deref(shretval).insert(deref(loc))
            inc(loc)
        return {{> types/CythonPythonType}}.create(cmove(shretval))

    def __or__(self, other):
        if not isinstance(self, {{> types/CythonPythonType}}):
            self = {{> types/CythonPythonType}}(self)
        if not isinstance(other, {{> types/CythonPythonType}}):
            other = {{> types/CythonPythonType}}(other)

        cdef shared_ptr[{{> types/CythonCppType}}] shretval = \
            make_shared[{{> types/CythonCppType}}]()

        cdef shared_ptr[{{> types/CythonCppType}}] cself = (<{{> types/CythonPythonType}}> self)._cpp_obj
        cdef shared_ptr[{{> types/CythonCppType}}] cother = (<{{> types/CythonPythonType}}> other)._cpp_obj

        cdef {{>types/CythonCppType}}.iterator loc = deref(cself).begin()
        while loc != deref(cself).end():
            deref(shretval).insert(deref(loc))
            inc(loc)
        loc = deref(cother).begin()
        while loc != deref(cother).end():
            deref(shretval).insert(deref(loc))
            inc(loc)
        return {{> types/CythonPythonType}}.create(cmove(shretval))

    def __xor__(self, other):
        if not isinstance(self, {{> types/CythonPythonType}}):
            self = {{> types/CythonPythonType}}(self)
        if not isinstance(other, {{> types/CythonPythonType}}):
            other = {{> types/CythonPythonType}}(other)

        cdef shared_ptr[{{> types/CythonCppType}}] shretval = \
            make_shared[{{> types/CythonCppType}}]()

        cdef shared_ptr[{{> types/CythonCppType}}] cself = (<{{> types/CythonPythonType}}> self)._cpp_obj
        cdef shared_ptr[{{> types/CythonCppType}}] cother = (<{{> types/CythonPythonType}}> other)._cpp_obj

        cdef {{>types/CythonCppType}}.iterator loc = deref(cself).begin()
        while loc != deref(cself).end():
            if deref(cother).count(deref(loc)) == 0:
                deref(shretval).insert(deref(loc))
            inc(loc)
        loc = deref(cother).begin()
        while loc != deref(cother).end():
            if deref(cself).count(deref(loc)) == 0:
                deref(shretval).insert(deref(loc))
            inc(loc)
        return {{> types/CythonPythonType}}.create(cmove(shretval))


    @staticmethod
    def __get_reflection__():
        return _types_reflection.get_reflection__{{> types/CythonPythonType}}()


Set.register({{> types/CythonPythonType}})
{{/type:set?}}
{{#type:map?}}
    @staticmethod
    cdef shared_ptr[{{> types/CythonCppType}}] _make_instance(object items) except *:
        cdef shared_ptr[{{> types/CythonCppType}}] c_inst = make_shared[{{> types/CythonCppType}}]()
        if items is not None:
            for key, item in items.items():
                {{#type:keyType}}
                {{#type:container?}}
                if key is None:
                    raise TypeError("None is not of type {{> types/PEP484Type}}")
                if not isinstance(key, {{> types/PythonType}}):
                    key = {{> types/PythonType}}(key)
                {{/type:container?}}
                {{^type:container?}}
                if not isinstance(key, {{> types/PythonIsInstanceType}}):
                    raise TypeError(f"{key!r} is not of type {{> types/PEP484Type}}")
                {{#type:integer?}}
                {{! inject cython int overflow checks }}
                key = <{{> types/CythonPythonType}}> key
                {{/type:integer?}}
                {{/type:container?}}
                {{/type:keyType}}
                {{#type:valueType}}
                {{#type:container?}}
                if item is None:
                    raise TypeError("None is not of type {{> types/PEP484Type}}")
                if not isinstance(item, {{> types/PythonType}}):
                    item = {{> types/PythonType}}(item)
                {{/type:container?}}
                {{^type:container?}}
                if not isinstance(item, {{> types/PythonIsInstanceType}}):
                    raise TypeError(f"{item!r} is not of type {{> types/PEP484Type}}")
                {{#type:integer?}}
                {{! inject cython int overflow checks }}
                item = <{{> types/CythonPythonType}}> item
                {{/type:integer?}}
                {{/type:container?}}
                {{/type:valueType}}

                deref(c_inst)[{{!
                    }}{{#type:keyType}}{{> CythonPythonToCppKey}}{{/type:keyType}}{{!
                    }}] = {{!
                    }}{{#type:valueType}}{{> CythonPythonToCppItem}}{{/type:valueType}}
        return c_inst

    def __getitem__(self, key):
        err = KeyError(f'{key}')
        if not self or key is None:
            raise err
        {{#type:keyType}}
        {{#type:container?}}
        try:
            if not isinstance(key, {{> types/PythonType}}):
                key = {{> types/PythonType}}(key)
        except Exception:
            raise err from None
        {{/type:container?}}
        if not isinstance(key, {{> types/PythonType}}):
            raise err from None
        {{/type:keyType}}
        cdef {{> types/CythonCppType}}.iterator iter = deref(
            self._cpp_obj).find({{#type:keyType}}{{> CythonPythonToCppKey}}{{/type:keyType}})
        if iter == deref(self._cpp_obj).end():
            raise err
        cdef {{#type:valueType}}{{!
        }}{{^type:simple?}}shared_ptr[{{/type:simple?}}{{> types/CythonCppType}}{{!
            }}{{^type:simple?}}]{{/type:simple?}}{{!
            }} citem = {{^type:simple?}}__reference_shared_ptr({{/type:simple?}}{{!
                }}deref(iter).second{{!
            }}{{^type:simple?}}, self._cpp_obj){{/type:simple?}}{{/type:valueType}}
        return {{#type:valueType}}{{!
            }}{{> ContainerCythonCppToPythonItem}}{{/type:valueType}}

    def __iter__(self):
        if not self:
            return
        cdef {{#type:keyType}}{{!
            }}{{^type:simple?}}shared_ptr[{{/type:simple?}}{{> types/CythonCppType}}{{!
            }}{{^type:simple?}}]{{/type:simple?}}{{!
            }}{{/type:keyType}} citem
        cdef {{>types/CythonCppType}}.iterator loc = deref(self._cpp_obj).begin()
        while loc != deref(self._cpp_obj).end():
            {{#type:keyType}}
            citem = {{^type:simple?}}__reference_shared_ptr({{/type:simple?}}{{!
                    }}deref(loc).first{{!
                    }}{{^type:simple?}}, self._cpp_obj){{/type:simple?}}
            yield {{> ContainerCythonCppToPythonItem}}
            {{/type:keyType}}
            inc(loc)

    def __contains__(self, key):
        if not self or key is None:
            return False
        {{#type:keyType}}
        {{#type:container?}}
        try:
            if not isinstance(key, {{> types/PythonType}}):
                key = {{> types/PythonType}}(key)
        except Exception:
            return False
        {{/type:container?}}
        if not isinstance(key, {{> types/PythonType}}):
            return False
        cdef {{> types/CythonCppType}} ckey = {{> CythonPythonToCppKey}}
        {{/type:keyType}}
        return deref(self._cpp_obj).count(ckey) > 0

    def get(self, key, default=None):
        if not self or key is None:
            return default
        {{! Convert the key once }}
        {{#type:keyType}}
        {{#type:container?}}
        try:
            if not isinstance(key, {{> types/PythonType}}):
                key = {{> types/PythonType}}(key)
        except Exception:
            return default
        {{/type:container?}}
        if not isinstance(key, {{> types/PythonType}}):
            return default
        {{/type:keyType}}
        if key not in self:
            return default
        return self[key]

    def values(self):
        if not self:
            return
        cdef {{#type:valueType}}{{!
          }}{{^type:simple?}}shared_ptr[{{/type:simple?}}{{> types/CythonCppType}}{{!
          }}{{^type:simple?}}]{{/type:simple?}}{{!
          }}{{/type:valueType}}{{!!
          }} citem
        cdef {{>types/CythonCppType}}.iterator loc = deref(self._cpp_obj).begin()
        while loc != deref(self._cpp_obj).end():
            {{#type:valueType}}
            citem = {{^type:simple?}}__reference_shared_ptr({{/type:simple?}}{{!
                    }}deref(loc).second{{!
                    }}{{^type:simple?}}, self._cpp_obj){{/type:simple?}}
            yield {{> ContainerCythonCppToPythonItem}}
            {{/type:valueType}}
            inc(loc)

    def items(self):
        if not self:
            return
        cdef {{#type:keyType}}{{!
          }}{{^type:simple?}}shared_ptr[{{/type:simple?}}{{> types/CythonCppType}}{{!
            }}{{^type:simple?}}]{{/type:simple?}}{{!
            }}{{/type:keyType}}{{!
          }} ckey
        cdef {{#type:valueType}}{{!
          }}{{^type:simple?}}shared_ptr[{{/type:simple?}}{{> types/CythonCppType}}{{!
            }}{{^type:simple?}}]{{/type:simple?}}{{!
            }}{{/type:valueType}} citem
        cdef {{>types/CythonCppType}}.iterator loc = deref(self._cpp_obj).begin()
        while loc != deref(self._cpp_obj).end():
            {{#type:keyType}}
            ckey = {{^type:simple?}}__reference_shared_ptr({{/type:simple?}}{{!
                    }}deref(loc).first{{!
                    }}{{^type:simple?}}, self._cpp_obj){{/type:simple?}}
            {{/type:keyType}}
            {{#type:valueType}}
            citem = {{^type:simple?}}__reference_shared_ptr({{/type:simple?}}{{!
                    }}deref(loc).second{{!
                    }}{{^type:simple?}} ,self._cpp_obj){{/type:simple?}}
            {{/type:valueType}}
            yield ({{!
              }}{{#type:keyType}}{{!
              }}{{> ContainerCythonCppToPythonKey}}{{!
              }}{{/type:keyType}}, {{!
              }}{{#type:valueType}}{{!
              }}{{> ContainerCythonCppToPythonItem}}{{!
              }}{{/type:valueType}})
            inc(loc)

    @staticmethod
    def __get_reflection__():
        return _types_reflection.get_reflection__{{> types/CythonPythonType}}()


Mapping.register({{> types/CythonPythonType}})
{{/type:map?}}

{{/program:containerTypes}}{{!
}}{{#program:constants}}
{{#constant:value}}{{> ConstantValue}}{{/constant:value}}{{/program:constants}}{{!
}}{{#program:typedefs}}
{{typedef:symbolic}} = {{#typedef:type}}{{> types/PythonType}}{{/typedef:type}}
{{/program:typedefs}}
{{#program:has_stream?}}

{{#program:stream_types}}
cdef class ClientBufferedStream__{{type:flat_name}}(ClientBufferedStream):

    @staticmethod
    cdef create(cClientBufferedStream[{{ > types/CythonCppType}}]& c_obj, __RpcOptions rpc_options):
        __fbthrift_inst = ClientBufferedStream__{{type:flat_name}}(rpc_options)
        __fbthrift_inst._gen = make_unique[cClientBufferedStreamWrapper[{{> types/CythonCppType}}]](c_obj)
        return __fbthrift_inst

    @staticmethod
    cdef void callback(
        cFollyTry[cOptional[{{> types/CythonCppType}}]]&& result,
        PyObject* userdata,
    ):
        cdef cOptional[{{> types/CythonCppType}}] opt_val
        cdef {{> types/CythonCppType}} _value
        stream, pyfuture, rpc_options = <object> userdata
        if {{#program:stream_exceptions}}{{!
        }}result.hasException[{{> types/CythonCppType}}]():
            pyfuture.set_exception({{!
            }}{{> types/CythonPythonType}}.create({{!
            }}thrift.py3.exceptions.try_make_shared_exception[{{> types/CythonCppType}}](result.exception()){{!
            }}))
        elif {{/program:stream_exceptions}}{{!
        }}result.hasException():
            pyfuture.set_exception(
                thrift.py3.exceptions.create_py_exception(result.exception(), <__RpcOptions>rpc_options)
            )
        else:
            opt_val = result.value()
            if opt_val.has_value():
                try:
                    _value = opt_val.value()
                    pyfuture.set_result({{> stream/CythonCppValueToPythonValue}})
                except Exception as ex:
                    pyfuture.set_exception(ex.with_trackback(None))
            else:
                pyfuture.set_exception(StopAsyncIteration())

    def __anext__(self):
        __loop = asyncio.get_event_loop()
        __future = __loop.create_future()
        __userdata = (self, __future, self._rpc_options)
        bridgeCoroTaskWith[cOptional[{{> types/CythonCppType}}]](
            self._executor,
            deref(self._gen).getNext(),
            ClientBufferedStream__{{type:flat_name}}.callback,
            <PyObject *>__userdata,
        )
        return asyncio.shield(__future)

cdef class ServerStream__{{type:flat_name}}(ServerStream):
    pass

{{/program:stream_types}}
{{#program:response_and_stream_types}}
cdef class ResponseAndClientBufferedStream__{{> stream/ResponseClassNameSuffix}}(ResponseAndClientBufferedStream):

    @staticmethod
    cdef create(cResponseAndClientBufferedStream[{{!
            }}{{#type:streamFirstResponseType}}{{ > types/CythonCppType}}{{/type:streamFirstResponseType}}, {{!
            }}{{#type:streamElemType}}{{ > types/CythonCppType}}{{/type:streamElemType}}]& c_obj, __RpcOptions rpc_options):
        __fbthrift_inst = ResponseAndClientBufferedStream__{{> stream/ResponseClassNameSuffix}}()
        __fbthrift_inst._stream = ClientBufferedStream__{{#type:streamElemType}}{{type:flat_name}}{{/type:streamElemType}}.create(
            c_obj.stream, rpc_options,
        )
{{#type:streamFirstResponseType}}
        cdef {{> types/CythonCppType}} _value = c_obj.response
        __fbthrift_inst._response = {{> stream/CythonCppValueToPythonValue}}
{{/type:streamFirstResponseType}}
        return __fbthrift_inst

    def __iter__(self):
        yield self._response
        yield self._stream

cdef class ResponseAndServerStream__{{> stream/ResponseClassNameSuffix}}(ResponseAndServerStream):
    pass

{{/program:response_and_stream_types}}
{{/program:has_stream?}}
